package com.ymx.darling.wrapper;

import com.ymx.darling.util.LambdaUtil;
import com.ymx.darling.util.StringUtil;
import com.ymx.darling.wrapper.fragment.lambda.LambdaGetter;
import com.ymx.darling.wrapper.fragment.lambda.WhereLambda;
import com.ymx.darling.wrapper.fragment.*;
import com.ymx.darling.bean.sql.SqlStatement;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * Wrapper条件包装器构建where范围的语句<br/>
 *
 * @author 兴哥的小新
 */
public abstract class Wrapper<T extends Wrapper<T>> {
    private final WhereSegment where = new WhereSegment();

    /**
     * 返回 拼接后的where的条件 包含Where关键字的拼接
     * @return SqlStatement
     */
    public SqlStatement customSqlSegment() {
        SqlStatement sqlStatement = this.where.getSqlStatement();
        String whereSql = sqlStatement.getSql();
        if (StringUtil.isNoNull(whereSql)) {
            String newSql = " WHERE " + whereSql;
            sqlStatement.setSql(newSql);
        }
        return sqlStatement;
    }


    /**
     * 返回 拼接后的where的条件 不包含Where关键字的拼接
     * @return SqlStatement
     */
    public SqlStatement sqlSegment() {
        return this.where.getSqlStatement();
    }


    /**
     * 返回sql不同关键字的参数语法
     */
    protected Map<String, SqlSegment> getSqlSegmentMap(){
        Map<String, SqlSegment> sqlSegmentMap = new HashMap<>();
        sqlSegmentMap.put("where", this.where);
        return sqlSegmentMap;
    }

    public T whereSegment(String sqlSnippet, Object... param){
        this.where.whereSegment(sqlSnippet, param);
        return (T)this;
    }

    public T columnEq(String table1Colum, String table2Colum){
       this.where.columnEq(table1Colum,table2Colum);
       return (T)this;
    }

    public <L1,L2> T columnEq(LambdaGetter<L1> table1Colum, LambdaGetter<L2> table2Colum){
        return this.columnEq(
                LambdaUtil.getColumnName(table1Colum),
                LambdaUtil.getColumnName(table2Colum)
        );
    }

    public T columnNe(String col1, String col2){
        this.where.columnNe(col1, col2);
        return (T)this;
    }

    public <L1,L2> T columnNe(LambdaGetter<L1> table1Colum, LambdaGetter<L2> table2Colum){
        return this.columnNe(
                LambdaUtil.getColumnName(table1Colum),
                LambdaUtil.getColumnName(table2Colum)
        );
    }

    public T columnGt(String col1, String col2){
        this.where.columnGt(col1, col2);
        return(T)this;
    }

    public <L1,L2> T columnGt(LambdaGetter<L1> table1Colum, LambdaGetter<L2> table2Colum){
        return this.columnGt(
                LambdaUtil.getColumnName(table1Colum),
                LambdaUtil.getColumnName(table2Colum)
        );
    }

    public T columnGe(String col1, String col2){
        this.where.columnGe(col1, col2);
        return(T)this;
    }

    public <L1,L2> T columnGe(LambdaGetter<L1> table1Colum, LambdaGetter<L2> table2Colum){
        return this.columnGe(
                LambdaUtil.getColumnName(table1Colum),
                LambdaUtil.getColumnName(table2Colum)
        );
    }

    public T columnLt(String col1, String col2){
        this.where.columnLt(col1, col2);
        return(T)this;
    }

    public <L1,L2> T columnLt(LambdaGetter<L1> table1Colum, LambdaGetter<L2> table2Colum){
        return this.columnLt(
                LambdaUtil.getColumnName(table1Colum),
                LambdaUtil.getColumnName(table2Colum)
        );
    }

    public T columnLe(String col1, String col2){
        this.where.columnLe(col1, col2);
        return (T)this;
    }

    public <L1,L2> T columnLe(LambdaGetter<L1> table1Colum, LambdaGetter<L2> table2Colum){
        return this.columnLe(
                LambdaUtil.getColumnName(table1Colum),
                LambdaUtil.getColumnName(table2Colum)
        );
    }

    public T bracket(WhereLambda lambda){
        this.where.bracket(lambda);
        return (T)this;
    }

    public T eq(String col, Object value){
        this.where.eq(col,value);
        return (T)this;
    }

    public <L> T eq(LambdaGetter<L> column, Object value) {
        return this.eq(LambdaUtil.getColumnName(column), value);
    }

    public T allEq(Map<String, Object> whereMap) {
        Objects.requireNonNull(whereMap, "进行allEq操作时传入的map不能为null");
        whereMap.forEach((column, value) -> this.eq(Objects.nonNull(value), column, value));
        return (T)this;
    }

    public <E> T eq(boolean condition, String col, E value){
        if (condition) {
            return this.eq(col, value);
        }
        return (T)this;
    }

    public <L,E> T eq(boolean condition, LambdaGetter<L> column, E value) {
        return this.eq(condition, LambdaUtil.getColumnName(column), value);
    }

    public T eqInSql(String col, String subSql, Object... param){
        this.where.eqInSql(col, subSql, param);
        return(T)this;
    }

    public <L> T eqInSql(LambdaGetter<L> column, String subSql, Object... param) {
        return this.eqInSql(LambdaUtil.getColumnName(column), subSql, param);
    }

    public T ne(String col, Object value){
        this.where.ne(col, value);
        return(T)this;
    }

    public <L> T ne(LambdaGetter<L> column, Object value) {
        return this.ne(LambdaUtil.getColumnName(column), value);
    }

    public <E> T ne(boolean condition, String col, E value){
        if (condition) {
            this.ne(col,value);
        }
        return (T)this;
    }

    public <L,E> T ne(boolean condition, LambdaGetter<L> column, E value) {
        return this.ne(condition, LambdaUtil.getColumnName(column), value);
    }

    public T neInSql(String col, String subSql, Object... param){
        this.where.notInSql(col, subSql, param);
        return(T)this;
    }

    public <L> T neInSql(LambdaGetter<L> column, String subSql, Object... param) {
        return this.neInSql(LambdaUtil.getColumnName(column), subSql, param);
    }

    public T and(){
        this.where.and();
        return(T)this;
    }

    public T and(WhereLambda lambda){
        this.where.and(lambda);
        return(T)this;
    }

    public T or(){
        this.where.or();
        return(T)this;
    }

    public T or(WhereLambda lambda){
        this.where.or(lambda);
        return(T)this;
    }

    public T gt(String colum, Object value){
        this.where.gt(colum,value);
        return(T)this;
    }

    public <L> T gt(LambdaGetter<L> column, Object value) {
        return this.gt(LambdaUtil.getColumnName(column), value);
    }

    public <E> T gt(boolean condition, String colum, E value){
        if (condition) {
            this.gt(colum,value);
        }
        return (T)this;
    }

    public <L,E> T gt(boolean condition, LambdaGetter<L> column, E value) {
        return this.gt(condition, LambdaUtil.getColumnName(column), value);
    }

    public T gtInSql(String colum, String subSql, Object... param){
        this.where.gtInSql(colum,subSql, param);
        return(T)this;
    }

    public <L> T gtInSql(LambdaGetter<L> column, String subSql, Object... param) {
        return this.gtInSql(LambdaUtil.getColumnName(column), subSql, param);
    }

    public T ge(String colum, Object value){
        this.where.ge(colum,value);
        return(T)this;
    }

    public <L> T ge(LambdaGetter<L> column, Object value) {
        return this.ge(LambdaUtil.getColumnName(column), value);
    }

    public <E> T ge(boolean condition, String colum, E value){
        if (condition) {
            this.where.ge(colum,value);
        }
        return (T)this;
    }

    public <L,E> T ge(boolean condition, LambdaGetter<L> column, E value) {
        return this.ge(condition, LambdaUtil.getColumnName(column), value);
    }

    public T geInSql(String colum, String subSql, Object... param){
        this.where.geInSql(colum,subSql, param);
        return(T)this;
    }

    public <L> T geInSql(LambdaGetter<L> column, String subSql, Object... param) {
        return this.geInSql(LambdaUtil.getColumnName(column), subSql, param);
    }

    public T lt(String colum, Object value){
        this.where.lt(colum,value);
        return(T)this;
    }

    public <L> T lt(LambdaGetter<L> column, Object value) {
        return this.lt(LambdaUtil.getColumnName(column), value);
    }

    public <E> T lt(boolean condition, String colum, E value){
        if (condition) {
            this.lt(colum,value);
        }
        return (T)this;
    }

    public <L,E> T lt(boolean condition, LambdaGetter<L> column, E value) {
        return this.lt(condition, LambdaUtil.getColumnName(column), value);
    }

    public T ltInSql(String colum, String subSql, Object... param){
        this.where.ltInSql(colum,subSql, param);
        return(T)this;
    }

    public <L> T ltInSql(LambdaGetter<L> column, String subSql, Object... param) {
        return this.ltInSql(LambdaUtil.getColumnName(column), subSql, param);
    }

    public T le(String colum, Object value){
        this.where.le(colum,value);
        return(T)this;
    }

    public <L> T le(LambdaGetter<L> column, Object value) {
        return this.le(LambdaUtil.getColumnName(column), value);
    }

    public <E> T le(boolean condition, String colum, E value){
        if (condition) {
            this.where.le(colum,value);
        }
        return (T)this;
    }

    public <L,E> T le(boolean condition, LambdaGetter<L> column, E value) {
        return this.le(condition, LambdaUtil.getColumnName(column), value);
    }

    public T leInSql(String colum, String subSql, Object... param){
        this.where.leInSql(colum,subSql, param);
        return(T)this;
    }

    public <L> T leInSql(LambdaGetter<L> column, String subSql, Object... param) {
        return this.leInSql(LambdaUtil.getColumnName(column), subSql, param);
    }

    public T between(String colum, Object one, Object two){
        this.where.between(colum,one,two);
        return(T)this;
    }

    public <L> T between(LambdaGetter<L> column, Object one, Object two) {
        return this.between(LambdaUtil.getColumnName(column), one, two);
    }

    public T notBetween(String colum, Object one, Object two){
        this.where.notBetween(colum, one, two);
        return (T)this;
    }

    public <L> T notBetween(LambdaGetter<L> column, Object one, Object two) {
        this.where.notBetween(column, one, two);
        return this.notBetween(LambdaUtil.getColumnName(column), one, two);
    }

    public T notIn(String colum, List<?> values) {
        this.where.notIn(colum, values);
        return(T)this;
    }

    public <L> T notIn(LambdaGetter<L> colum, List<?> values) {
        return this.notIn(LambdaUtil.getColumnName(colum), values);
    }

    public T in(String colum, List<?> values){
        this.where.in(colum,values);
        return(T)this;
    }

    public <L> T in(LambdaGetter<L> colum, List<?> values) {
        return this.in(LambdaUtil.getColumnName(colum), values);
    }

    public T exists(String sql, Object... param) {
        this.where.exists(sql, param);
        return(T)this;
    }

    public T notExists(String sql, Object... param) {
        this.where.notExists(sql, param);
        return(T)this;
    }

    public T like(String colum , String value){
        this.where.like(colum,value);
        return(T)this;
    }

    public <L> T like(LambdaGetter<L> column, String value) {
        return this.like(LambdaUtil.getColumnName(column), value);
    }

    public T like(boolean condition, String colum , String value){
        if (condition) {
            this.like(colum,value);
        }
        return (T)this;
    }

    public <L> T like(boolean condition, LambdaGetter<L> column, String value) {
        return this.like(condition, LambdaUtil.getColumnName(column), value);
    }

    public T notLike(String colum , String value){
        this.where.notLike(colum, value);
        return (T)this;
    }

    public <L> T notLike(LambdaGetter<L> colum , String value){
        this.where.notLike(colum, value);
        return (T)this;
    }

    public T leftLike(String colum , String value){
        this.where.leftLike(colum,value);
        return(T)this;
    }

    public <L> T leftLike(LambdaGetter<L> column, String value) {
        return this.leftLike(LambdaUtil.getColumnName(column), value);
    }

    public <L> T leftLike(boolean condition, LambdaGetter<L> column, String value) {
        return this.leftLike(condition, LambdaUtil.getColumnName(column), value);
    }

    public T leftLike(boolean condition, String colum , String value){
        if (condition) {
            this.where.leftLike(colum, value);
        }
        return (T)this;
    }

    public T notLeftLike(String colum , String value){
        this.where.notLeftLike(colum, value);
        return (T)this;
    }

    public <L> T notLeftLike(LambdaGetter<L> colum , String value){
        this.where.notLeftLike(colum, value);
        return (T)this;
    }

    public T rightLike(String colum , String value){
        this.where.rightLike(colum,value);
        return(T)this;
    }

    public <L> T rightLike(LambdaGetter<L> column, String value) {
        return this.rightLike(LambdaUtil.getColumnName(column), value);
    }

    public T rightLike(boolean condition, String colum , String value){
        if (condition) {
            this.where.rightLike(colum, value);
        }
        return (T)this;
    }

    public <L> T rightLike(boolean condition, LambdaGetter<L> column, String value) {
        return this.rightLike(condition, LambdaUtil.getColumnName(column), value);
    }

    public T notRightLike(String colum , String value){
        this.where.notRightLike(colum, value);
        return (T)this;
    }

    public <L> T notRightLike(LambdaGetter<L> colum , String value){
        this.where.notRightLike(colum, value);
        return (T)this;
    }

    public T isNull(String colum){
        this.where.isNull(colum);
        return(T)this;
    }

    public <L> T isNull(LambdaGetter<L> column) {
        return this.isNull(LambdaUtil.getColumnName(column));
    }

    public T isNotNull(String colum){
        this.where.isNotNull(colum);
        return(T)this;
    }

    public <L> T isNotNull(LambdaGetter<L> column) {
        return this.isNotNull(LambdaUtil.getColumnName(column));
    }

    public T inSql(String colum, String sql, Object... param){
        this.where.inSql(colum,sql, param);
        return(T)this;
    }

    public <L> T inSql(LambdaGetter<L> column, String subSql, Object... param) {
        return this.inSql(LambdaUtil.getColumnName(column), subSql, param);
    }

    public T notInSql(String colum, String sql, Object... param){
        this.where.notInSql(colum,sql, param);
        return(T)this;
    }

    public <L> T notInSql(LambdaGetter<L> column, String subSql, Object... param) {
        return this.notInSql(LambdaUtil.getColumnName(column), subSql, param);
    }
}

